#include <functional>
#include <iostream>
#include <cstdio>
#include <regex>

#include "command-func.hpp"
#include "game.hpp"
#include "displayer.hpp"
#include "util.hpp"
#include "display_str.hpp"

using std::cerr;
using std::cin;
using std::cout;
using std::endl;

std::string CommandFuncPreset(std::string cmd, Game *g)
{
    static auto pat = std::regex("\\s*(?:user\\s+[AQSJ]{2,4}|map\\s+\\d+\\s+[AQSJ]\\s+[0-3]|fund\\s+[AQSJ]\\s+\\d+|credit\\s+[AQSJ]\\s+\\d+|gift\\s+[AQSJ]\\s+(barrier|robot|god)\\s+\\d+|barrier\\s+\\d+|userloc\\s+[AQSJ]\\s+\\d+\\s+\\d+|nextuser\\s+[AQSJ])\\s*(#.*)?$", std::regex::icase);
    static auto pat_user = std::regex("\\s*(user)\\s+([AQSJ]{2,4})\\s*(#.*)?$", std::regex::icase);
    static auto pat_map = std::regex("\\s*(map)\\s+(\\d+)\\s+([AQSJ])\\s+([0-3])\\s*(#.*)?$", std::regex::icase);
    static auto pat_fund = std::regex("\\s*(fund)\\s+([AQSJ])\\s+(\\d+)\\s*(#.*)?$", std::regex::icase);
    static auto pat_credit = std::regex("\\s*(credit)\\s+([AQSJ])\\s+(\\d+)\\s*(#.*)?$", std::regex::icase);
    static auto pat_gift = std::regex("\\s*(gift)\\s+([AQSJ])\\s+(barrier|robot|god)\\s+(\\d+)\\s*(#.*)?$", std::regex::icase);
    static auto pat_prop = std::regex("\\s*(barrier)\\s+(\\d+)\\s*(#.*)?$", std::regex::icase);
    static auto pat_loc = std::regex("\\s*(userloc)\\s+([AQSJ])\\s+(\\d+)\\s+(\\d+)\\s*(#.*)?$", std::regex::icase);
    static auto pat_next = std::regex("\\s*(nextuser)\\s+([AQSJ])\\s*(#.*)?$", std::regex::icase);

    cmd = to_lower(cmd);
    if (!std::regex_match(cmd, pat))
    {
        g->SetTip(CmdErrorStr);
        cerr << "cmd error: unknown" << endl;
        return "cmd error: unknown";
    }
    std::smatch result;
    std::string ctr;
    std::stringstream ss(cmd);
    ss >> ctr;
    ctr = to_lower(ctr);
    bool res = false;
    if (ctr == "user" && std::regex_match(cmd, result, pat_user))
        res = g->SetUser(result.str(2));
    else if (g->m_user_num > 0)
    {
        if (ctr == "map" && std::regex_match(cmd, result, pat_map))
        {
            auto loc = std::stoi(result.str(2));
            auto person = result.str(3)[0];
            auto level = std::stoi(result.str(4));
            res = g->SetLandOwner(person, loc, level);
        }
        else if (ctr == "fund" && std::regex_match(cmd, result, pat_fund))
        {
            auto person = result.str(2)[0];
            auto fund = std::stoi(result.str(3));
            res = g->SetFund(person, fund);
        }
        else if (ctr == "credit" && std::regex_match(cmd, result, pat_credit))
        {
            auto person = result.str(2)[0];
            auto credit = std::stoi(result.str(3));
            res = g->SetCredit(person, credit);
        }
        else if (ctr == "gift" && std::regex_match(cmd, result, pat_gift))
        {
            auto person = result.str(2)[0];
            auto gift_type = result.str(3);
            auto num = std::stoi(result.str(4));
            if (gift_type == "barrier")
                res = g->SetBarrierNum(person, num);
            else if (gift_type == "robot")
                res = g->SetRobotNum(person, num);
            else if (gift_type == "god")
                res = g->SetGodNum(person, num);
        }
        else if (ctr == "barrier" && std::regex_match(cmd, result, pat_prop))
        {
            auto loc = std::stoi(result.str(2));
            res = g->PlaceBarrier(loc);
        }
        else if (ctr == "userloc" && std::regex_match(cmd, result, pat_loc))
        {
            auto person = result.str(2)[0];
            auto loc = std::stoi(result.str(3));
            auto delay_num = std::stoi(result.str(4));
            res = g->SetUserLocation(person, loc, delay_num);
        }
        else if (ctr == "nextuser" && std::regex_match(cmd, result, pat_next))
        {
            auto person = result.str(2)[0];
            res = g->SetCurrentUser(person);
        }
        else
        {
            g->SetTip(CmdErrorStr);
            cerr << "cmd error: unknown cmd or arg" << endl;
            return "cmd error: unknown cmd or arg";
        }
    }
    if (!res)
    {
        g->SetTip(CmdErrorStr);
        cerr << "cmd error: " << cmd << endl;
        return "cmd error: " + cmd;
    }

    return "ok";
}

std::string CommandFuncDump(std::string cmd, Game *g_ptr)
{
    static std::regex pat("\\s*(#.*)?$");
    Game &g = *g_ptr;
    if (!std::regex_match(cmd, pat))
    {
        g.SetTip(CmdErrorStr);
        cerr << "cmd error: " << cmd << endl;
        return "cmd error: " + cmd;
    }
    if (g.m_user_num == 0)
        g.SetUser("AQSJ");

    cerr << "user " << g.m_user_order << endl;
    cerr << "nextuser " << g.m_user_order[g.m_current_user] << endl;
    for (size_t i = 0; i < LAND_NUM; ++i)
    {
        Land &land = g.m_land[i];
        if (land.owner)
            cerr << "map " << int(i) << " " << char(land.owner) << " " << int(land.level) << endl;
        if (land.barrier)
            cerr << "barrier " << int(i) << endl;
    }
    for (size_t i = 0; i < g.m_user_num; ++i)
    {
        Actor &actor = g.m_actor[i];
        cerr << "userloc " << actor.m_name << " " << actor.m_loc << " " << actor.m_inMagic << endl;
        cerr << "fund " << actor.m_name << " " << (actor.m_bankruptcy ? -1 : actor.m_fund) << endl;
        cerr << "credit " << actor.m_name << " " << actor.m_credit << endl;
        if (actor.m_barrier > 0)
            cerr << "gift " << actor.m_name << " barrier " << actor.m_barrier << endl;
        if (actor.m_robot > 0)
            cerr << "gift " << actor.m_name << " robot " << actor.m_robot << endl;
        if (actor.m_god > 0)
            cerr << "gift " << actor.m_name << " god " << actor.m_god << endl;
    }
    exit(0);

    return "ok";
}

std::string CommandFuncQuit(std::string cmd, Game *g_ptr)
{
    static std::regex pat("\\s*(#.*)?$");
    Game &g = *g_ptr;
    if (!std::regex_match(cmd, pat))
    {
        g.SetTip(CmdErrorStr);
        cerr << "cmd error: " << cmd << endl;
        return "cmd error: " + cmd;
    }
    exit(0);
    return "ok";
}

std::string CommandFuncHelp(std::string cmd, Game *g_ptr)
{
    static std::regex pat("\\s*(#.*)?$");
    Game &g = *g_ptr;
    if (!std::regex_match(cmd, pat))
    {
        g.SetTip(CmdErrorStr);
        cerr << "cmd error: " << cmd << endl;
        return "cmd error: " + cmd;
    }
    g_ptr->FullScreenTip(HELPStr);
    return "ok";
}

int main(int argc, char **argv)
{
    Game g;
    Displayer d(g);
    g.SetDisplayer(&d);
    g.m_cfunc.AddFunc("preset", std::bind(CommandFuncPreset, std::placeholders::_1, &g));
    g.m_cfunc.AddFunc("dump", std::bind(CommandFuncDump, std::placeholders::_1, &g));
    g.m_cfunc.AddFunc("quit", std::bind(CommandFuncQuit, std::placeholders::_1, &g));
    g.m_cfunc.AddFunc("help", std::bind(CommandFuncHelp, std::placeholders::_1, &g));

    std::string line;
    int default_fund = 0;
    while (!g.Ready())
    {
        g.SetInputTip(InitFundTip);
        std::getline(cin, line);
        auto res = g.m_cfunc.ParaseAndCall(line);
        if (res.length() == 0)
        {
            std::stringstream ss(line);
            default_fund = 0;
            ss >> default_fund;
            if (default_fund >= 1000 && default_fund <= 50000)
            {
                g.SetDefaultFund(default_fund);
                break;
            }
        }
    }
    while (!g.Ready())
    {
        g.SetInputTip(ChoosePlayerTip);
        std::getline(cin, line);
        auto res = g.m_cfunc.ParaseAndCall(line);
        if (res.length() == 0)
        {
            for (auto &e : line)
            {
                if (e == '1')
                    e = 'Q';
                if (e == '2')
                    e = 'A';
                if (e == '3')
                    e = 'S';
                if (e == '4')
                    e = 'J';
            }
            if (g.SetUser(line))
                break;
        }
    }

    while (g.Step())
        ;
    while (true)
    {
        std::getline(cin, line);
        g.m_cfunc.ParaseAndCall(line);
    }

    return 0;
}